package com.xyzwps.paimon.furina.infra

import com.xyzwps.paimon.furina.common.QueryParams
import com.xyzwps.paimon.furina.common.value.PageSize
import io.swagger.v3.oas.annotations.enums.ParameterIn.*
import org.springdoc.core.fn.builders.operation.Builder
import org.springdoc.core.fn.builders.parameter.Builder.parameterBuilder
import org.springdoc.core.fn.builders.apiresponse.Builder.responseBuilder
import org.springdoc.core.fn.builders.requestbody.Builder.requestBodyBuilder
import org.springdoc.core.fn.builders.schema.Builder.schemaBuilder
import org.springframework.http.MediaType
import org.springframework.web.servlet.function.RequestPredicate
import org.springframework.web.servlet.function.RequestPredicates.accept;
import org.springdoc.core.fn.builders.content.Builder.contentBuilder;
import org.springdoc.core.fn.builders.arrayschema.Builder.arraySchemaBuilder;

enum class ParameterType(val type: String, val format: String) {
    STRING(type = "string", format = "string"),
    INT32(type = "integer", format = "int32"),
    INT64(type = "integer", format = "int64"),
}

fun makeDescription(description: String = "", shouldAuth: Boolean = false): String {
    val sb = StringBuilder()
    if (description.isNotBlank()) {
        sb.append(description).append("\n\n")
    }

    sb.append("## Requirements\n")
    sb.append("| Item           | Desc |\n")
    sb.append("|----------------|------|\n")
    sb.append("| Authentication | ${if (shouldAuth) "" else "Not "}Required |\n")

    return sb.toString()
}

fun Builder.desc(description: String = "", shouldAuth: Boolean = false): Builder =
    this.description(makeDescription(description, shouldAuth))

fun Builder.pathVar(
    name: String,
    required: Boolean = false,
    description: String? = null,
    example: String? = null,
    allowEmptyValue: Boolean = false,
    type: ParameterType = ParameterType.STRING
): Builder = this.parameter(
    parameterBuilder().`in`(PATH).name(name).required(required).description(description).example(example)
        .allowEmptyValue(allowEmptyValue)
        .schema(schemaBuilder().type(type.type).format(type.format))
)

fun Builder.query(
    name: String,
    required: Boolean = true,
    description: String? = null,
    example: String? = null,
    allowEmptyValue: Boolean = false,
    type: ParameterType = ParameterType.STRING
): Builder = this.parameter(
    parameterBuilder().`in`(QUERY).name(name).required(required).description(description).example(example)
        .allowEmptyValue(allowEmptyValue)
        .schema(schemaBuilder().type(type.type).format(type.format))
)

fun Builder.queryPageSize(description: String = "Page size"): Builder = this.parameter(
    parameterBuilder().`in`(QUERY)
        .name(QueryParams.SIZE)
        .required(false)
        .description(description)
        .example("${PageSize.DEFAULT.value}")
        .allowEmptyValue(true)
        .schema(
            schemaBuilder()
                .format(ParameterType.INT32.format)
                .type(ParameterType.INT32.type)
                .minimum("${PageSize.MIN}").maximum("${PageSize.MAX}")
        )
)

fun Builder.reqBody(body: Class<*>): Builder = this
    .requestBody(
        requestBodyBuilder()
            .content(
                contentBuilder()
                    .mediaType(MediaType.APPLICATION_JSON_VALUE)
                    .schema(schemaBuilder().implementation(body))
            )
    )

fun Builder.respOK(body: Class<*>): Builder = this
    .response(
        responseBuilder().responseCode("200").content(
            contentBuilder()
                .mediaType(MediaType.APPLICATION_JSON_VALUE)
                .schema(schemaBuilder().implementation(body))
        )
    )

fun Builder.respArray(body: Class<*>): Builder = this
    .response(
        responseBuilder()
            .responseCode("200")
            .content(
                contentBuilder().mediaType(MediaType.APPLICATION_JSON_VALUE)
                    .array(arraySchemaBuilder().schema(schemaBuilder().implementation(body)))
            )
    )


val acceptJson: RequestPredicate = accept(MediaType.APPLICATION_JSON)